package com.github.glomadrian.materialanimatedswitch.painter;

import com.github.glomadrian.materialanimatedswitch.MaterialAnimatedSwitchState;
import com.github.glomadrian.materialanimatedswitch.observer.BallFinishObservable;
import com.github.glomadrian.materialanimatedswitch.observer.BallMoveObservable;
import com.github.glomadrian.materialanimatedswitch.utils.AnimatorValueUtils;
import com.github.glomadrian.materialanimatedswitch.utils.Utils;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;

public class BallPainter implements SwitchInboxPinnedPainter {
    protected Paint paint;
    protected Paint toBgPainter;
    protected int padding;
    protected int height;
    protected int width;
    protected int radius;
    protected int ballPositionX;
    protected int ballStartPositionX;
    protected int ballMovementRange;
    private AnimatorValue toAnimator;
    private AnimatorValue backAnimator;
    private AnimatorValue colorAnimator;
    private MaterialAnimatedSwitchState actualState;
    private BallFinishObservable ballFinishObservable;
    private BallMoveObservable ballMoveObservable;
    private Context context;
    private int middle;
    private int bgColor;
    private int toBgColor;

    public BallPainter(int bgColor, int toBgColor, int padding,
                       BallFinishObservable ballFinishObservable,
                       BallMoveObservable ballMoveObservable,
                       Context context) {
        this.bgColor = bgColor;
        this.toBgColor = toBgColor;
        this.padding = padding;
        this.ballFinishObservable = ballFinishObservable;
        this.ballMoveObservable = ballMoveObservable;
        this.context = context;
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setColor(new Color(bgColor));
        paint.setStyle(Paint.Style.FILL_STYLE);
        paint.setAntiAlias(true);
        toBgPainter = new Paint();
        toBgPainter.setColor(new Color(toBgColor));
        toBgPainter.setStyle(Paint.Style.FILL_STYLE);
        toBgPainter.setAntiAlias(true);
        toBgPainter.setAlpha(0);
        radius = Utils.vp2px(context, 11);
        ballStartPositionX = Utils.vp2px(context, 12);
        ballPositionX = padding;
    }

    private void initAnimator() {
        int from = padding;
        int to = width - padding;
        ballMovementRange = to - from;
        toAnimator = new AnimatorValue();
        toAnimator.setValueUpdateListener(new BallAnimatorListener());
        toAnimator.setStateChangedListener(new BallAnimatorFinishListener());

        backAnimator = new AnimatorValue();
        backAnimator.setValueUpdateListener(new BallAnimatorListener());
        backAnimator.setStateChangedListener(new BallAnimatorFinishListener());
    }

    private void initColorAnimator() {
        colorAnimator = new AnimatorValue();
        colorAnimator.setDuration(ballMovementRange);
        colorAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                toBgPainter.setAlpha(v);
            }
        });
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.drawCircle(ballPositionX, middle, radius, paint);
        canvas.drawCircle(ballPositionX, middle, radius, toBgPainter);
    }

    @Override
    public int getColor() {
        return bgColor;
    }

    @Override
    public void setColor(int color) {
        this.bgColor = color;
    }

    @Override
    public void onSizeChanged(int height, int width) {
        this.height = height;
        this.width = width;
        middle = height / 2;
        initAnimator();
        initColorAnimator();
    }

    @Override
    public void setState(MaterialAnimatedSwitchState state) {
        switch (state) {
            case PRESS:
                actualState = MaterialAnimatedSwitchState.PRESS;
                if (toAnimator.isRunning()) {
                    toAnimator.resume();
                }
                toAnimator.start();
                break;
            case RELEASE:
                actualState = MaterialAnimatedSwitchState.RELEASE;
                if (backAnimator.isRunning()) {
                    backAnimator.resume();
                }
                backAnimator.start();
                break;
            default:
                break;
        }
    }

    private class BallAnimatorFinishListener implements Animator.StateChangedListener {
        @Override
        public void onStart(Animator animator) {
            ballFinishObservable.setBallState(BallFinishObservable.BallState.MOVE);
        }

        @Override
        public void onStop(Animator animator) {
        }

        @Override
        public void onCancel(Animator animator) {
        }

        @Override
        public void onEnd(Animator animator) {
            if (actualState.equals(MaterialAnimatedSwitchState.PRESS)) {
                ballFinishObservable.setBallState(BallFinishObservable.BallState.PRESS);
            } else {
                ballFinishObservable.setBallState(BallFinishObservable.BallState.RELEASE);
            }
        }

        @Override
        public void onPause(Animator animator) {
        }

        @Override
        public void onResume(Animator animator) {
        }
    }

    private class BallAnimatorListener implements AnimatorValue.ValueUpdateListener {
        @Override
        public void onUpdate(AnimatorValue animatorValue, float v) {
            float value = v;
            if (actualState == MaterialAnimatedSwitchState.PRESS) {
                value = AnimatorValueUtils.getAnimatedValue(v, padding, width - padding);
                toBgPainter.setAlpha(v);
                ballMoveObservable.setBallAnimationValue(v);
            } else {
                value = AnimatorValueUtils.getAnimatedValue(v, width - padding, padding);
                toBgPainter.setAlpha(1f - v);
                ballMoveObservable.setBallAnimationValue(1f - v);
            }

            // Move the ball
            ballPositionX = (int) value;

            // 1- Get pixel of movement from 0 to movementRange
            int pixelMove = (int) (value - padding);

            // Transform the range movement to a 0 - 100 range
            int rangeValue = getAnimatedRange(pixelMove);

            // Set ball position to
            ballMoveObservable.setBallPosition(ballPositionX);
        }

        private int getAnimatedRange(int value) {
            return ((value * 100) / ballMovementRange);
        }
    }
}
